// use std::ops::{Neg,Index,IndexMut,AddAssign,MulAssign,DivAssign};
use std::ops::*;
use std::fmt;
use crate::core::rtweekend::*;


// 一、Vec3New class
#[derive(Debug,PartialEq,Copy,Clone)]
pub struct Vec3New{
    e:[f64;3],
}

impl Vec3New {
    pub fn new() -> Vec3New {
        Vec3New{e:[0.0,0.0,0.0]}
    }
    pub fn from(e0:f64, e1:f64, e2:f64) -> Vec3New{
        Vec3New{e:[e0,e1,e2]}
    }

    pub fn get_x(&self) -> f64{
        self.e[0]
    }
    pub fn get_y(&self) -> f64{
        self.e[1]
    }
    pub fn get_z(&self) -> f64{
        self.e[2]
    }

    pub fn length(&self) -> f64{
        self.length_squared().sqrt()
    }

    pub fn length_squared(&self) -> f64{
        self.e[0]*self.e[0] + self.e[1]*self.e[1] + self.e[2]*self.e[2]
    }

    // 在单位半径球内选择随机点
    pub fn random_point() -> Point3{
        Point3{
            e:[random_f64(), random_f64(), random_f64()]
        }
    }
    pub fn random_point_minmax(min:f64, max:f64) -> Point3{
        Point3{
            e:[random_range_f64(min, max), random_range_f64(min, max), random_range_f64(min, max)]
        }
    }

    // 用于保证散射方向与法线方向不会完全相反
    pub fn near_zero(&self) -> bool{
        const S: f64 = 1e-8;
        self.e[0].abs() < S && self.e[1].abs() < S && self.e[2].abs() < S
    }
}

impl std::ops::Neg for Vec3New{
    type Output = Self;
    fn neg(self) -> Self::Output{
        Self::from(-self.e[0],-self.e[1],-self.e[2])
    }
}

impl std::ops::Index<usize> for Vec3New{
    type Output = f64;
    fn index(&self, i: usize) ->&Self::Output{
        &self.e[i]
    }
}

impl std::ops::IndexMut<usize> for Vec3New{
    fn index_mut(&mut self, i:usize) -> &mut Self::Output{
        &mut self.e[i]
    }
}

// +=
impl AddAssign for Vec3New{
    fn add_assign(&mut self, other:Self){

        // 这是按照官方文档中的案例实现
        // 这里的*self同样是对原对象进行的修改
        // 通过解引用（这里self是对原对象的可变引用）获得实际值，然后进行重新赋值操作
        // *self = Self{
        //     e:[self.e[0]+other.e[0],self.e[1]+other.e[1],self.e[2]+other.e[2]]
        // }

        // +=操作是对原对象的修改
        self.e[0] += other.e[0];
        self.e[1] += other.e[1];
        self.e[2] += other.e[2];
    }
}

// *=
impl MulAssign<f64> for Vec3New{
    fn mul_assign(&mut self, rhs: f64) {
        self.e[0] *= rhs;
        self.e[1] *= rhs;
        self.e[2] *= rhs;
    }
}

// /=
impl DivAssign<f64> for Vec3New{
    fn div_assign(&mut self, rhs: f64){
        *self *= (1 as f64)/rhs;
        // self.e[0] /= rhs;
        // self.e[1] /= rhs;
        // self.e[2] /= rhs;
    }
}


// 二、utility functions
// Display Function
impl fmt::Display for Vec3New{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result{
        write!(f, "{} {} {}", self.e[0], self.e[1], self.e[2])
    }
}

// +
impl Add for Vec3New{
    type Output = Self;
    fn add(self, other: Self) -> Self::Output{
        Self{e:[self.e[0]+other.e[0], self.e[1]+other.e[1], self.e[2]+other.e[2]]}
    }
}
impl Add<&Vec3New> for &Vec3New{
    type Output = Vec3New;
    fn add(self, other: &Vec3New) -> Self::Output{
        Vec3New {e:[self.e[0]+other.e[0], self.e[1]+other.e[1], self.e[2]+other.e[2]]}
    }
}
impl Add<&Vec3New> for Vec3New{
    type Output = Vec3New;
    fn add(self, other: &Vec3New) -> Self::Output{
        Vec3New {e:[self.e[0]+other.e[0], self.e[1]+other.e[1], self.e[2]+other.e[2]]}
    }
}

// -
impl Sub for Vec3New{
    type Output = Self;
    fn sub(self, other: Self) -> Self::Output{
        Self {e:[self.e[0]-other.e[0], self.e[1]-other.e[1], self.e[2]-other.e[2]]}
    }
}
// Output类型是一种让编译器知道add方法返回类型的方式，在Add Trait中有定义https://doc.rust-lang.org/src/core/ops/arith.rs.html#227-231。
impl Sub<&Vec3New> for &Vec3New{
    type Output = Vec3New;
    fn sub(self, other: &Vec3New) -> Self::Output{
        Vec3New {e:[self.e[0]-other.e[0], self.e[1]-other.e[1], self.e[2]-other.e[2]]}
    }
}

impl Sub<&Vec3New> for Vec3New{
    type Output = Vec3New;
    fn sub(self, other: &Vec3New) -> Self::Output{
        Vec3New {e:[self.e[0]-other.e[0], self.e[1]-other.e[1], self.e[2]-other.e[2]]}
    }
}

impl Sub<Vec3New> for &Vec3New{
    type Output = Vec3New;
    fn sub(self, other: Vec3New) -> Self::Output{
        Vec3New {e:[self.e[0]-other.e[0], self.e[1]-other.e[1], self.e[2]-other.e[2]]}
    }
}

// *
impl Mul<&Vec3New> for &Vec3New{
    type Output = Vec3New;
    fn mul(self, other: &Vec3New) -> Self::Output{
        Vec3New {e:[self.e[0]*other.e[0], self.e[1]*other.e[1], self.e[2]*other.e[2]]} 
    }
}
impl Mul<f64> for &Vec3New{
    type Output = Vec3New;
    fn mul(self, other: f64) -> Self::Output{
        Vec3New {e:[other*self.e[0], other*self.e[1], other*self.e[2]]} 
    }
}
impl Mul<&Vec3New> for f64{
    type Output = Vec3New;
    fn mul(self, other: &Vec3New) -> Self::Output{
        other * (self as f64)
    }
}

// /
impl Div<f64> for &Vec3New{
    type Output = Vec3New;
    fn div(self, other: f64) -> Self::Output{
        (1_f64/other) * self
    }
}

// dot
pub fn dot(u: &Vec3New, v: &Vec3New) -> f64{
    u.e[0]*v.e[0]+u.e[1]*v.e[1]+u.e[2]*v.e[2]
}

// cross
pub fn cross(u: &Vec3New, v: &Vec3New) -> Vec3New{
    Vec3New {e:[
        u.e[1] * v.e[2] - u.e[2] * v.e[1],
        u.e[2] * v.e[0] - u.e[0] * v.e[2],
        u.e[0] * v.e[1] - u.e[1] * v.e[0]
    ]}
}

// 单位向量
pub fn unit_vector(v: &Vec3New) -> Vec3New{
    v / v.length()
}

// 在光线撞击点构建一个随机单位球体
pub fn random_in_unit_sphere() ->Point3{
    loop {
        let p = Point3::random_point_minmax(-1_f64, 1_f64);
        if p.length_squared() >=1_f64 {
            continue;
        }
        return p;
    }
}

pub fn random_unit_vector() -> Vec3New{
    unit_vector(&random_in_unit_sphere())
}

pub fn random_in_hemisphere(normal: &Vec3New) -> Vec3New{
    let in_unit_sphere = random_in_unit_sphere();
    if dot(&in_unit_sphere,normal) > 0_f64{
        return in_unit_sphere;
    }else{
        return -in_unit_sphere;
    }
}

pub fn reflect(v: &Vec3New, n: &Vec3New) -> Vec3New{
    v - 2_f64*dot(v, n)*n
}

// uv是入射光线， n是法线，最后一个是两种介质的折射率比
pub fn refract(uv: &Vec3New, n:&Vec3New, etai_over_etat: f64) -> Vec3New{
    let cos_theta = f64::min(dot(&((-1_f64)*uv), &n), 1_f64);
    // let cos_theta = dot(&((-1_f64)*uv), &n);
    let r_out_perp = etai_over_etat * &(uv + &(cos_theta*n));
    // let r_out_parallel = (-1_f64)*((1.0_f64-r_out_perp.length_squared()).abs().sqrt())*n;
    // let r_out_parallel = -f64::sqrt(f64::abs(1.0_f64 - r_out_perp.length_squared())) * n;
    let r_out_parallel = -f64::sqrt(1.0-etai_over_etat*etai_over_etat*(1.0-cos_theta*cos_theta)) * n;

    &r_out_perp + &r_out_parallel

    // let v = unit_vector(&uv);
    // let dt = dot(&uv, &n);
    // let disc = 1.0_f64 - etai_over_etat * etai_over_etat * (1.0_f64 - dt * dt);
    // etai_over_etat*&(&v - dt * n) - disc.sqrt() * n
}

pub fn random_in_unit_disk() -> Vec3New{
    let rng = random_f64();
    loop {
        let p = Vec3New::from(random_range_f64(-1.0, 1.0), random_range_f64(-1.0, 1.0), 0.0);
        if p.length_squared() >= 1.0{
            continue;
        }else{
            return p;
        }
    }
}

pub type Point3 = Vec3New;
pub type Color3 = Vec3New;

#[cfg(test)]
mod tests {
    // 注意这个惯用法：在 tests 模块中，从外部作用域导入所有名字。
    use super::*;

    #[test]
    fn test_new() {
        let v1 = Vec3New::new();
        assert_eq!(v1.e, [0.0, 0.0, 0.0]);
    }
    #[test]
    fn test_from(){
        let v2 = Vec3New::from(1.0,2.0,3.0);
        assert_eq!(v2.e,[1.0,2.0,3.0])
    }
    #[test]
    fn test_xyz(){
        let v3 = Vec3New::new();
        assert_eq!(v3.get_x(),0.0);
        assert_eq!(v3.get_y(),0.0);
        assert_eq!(v3.get_z(),0.0);
    }
    #[test]
    fn test_negindex(){
        let v4 = Vec3New::from(1.0,2.0,3.0);
        let mut negv = v4.neg();
        assert_eq!(negv.get_x(),-1.0);
        assert_eq!(*negv.index(0), -1.0 as f64);
        assert_eq!(*negv.index_mut(0)+1.0, 0.0 as f64);
    }
    #[test]
    fn test_addassign(){
        let mut v5 = Vec3New::from(1.0,1.0,1.0);
        v5 += Vec3New{e:[1.0,1.0,1.0]};
        assert_eq!(v5,Vec3New{e:[2.0,2.0,2.0]});
    }
    #[test]
    fn test_mulassign(){
        let mut v6 = Vec3New::from(1.0,2.0,3.0);
        v6 *= 3.0;
        assert_eq!(v6, Vec3New{e:[3.0, 6.0, 9.0]});
    }
    #[test]
    fn test_divassign(){
        let mut v7 = Vec3New::from(3.0,6.0,9.0);
        v7 /= 3.0;
        assert_eq!(v7, Vec3New{e:[1.0, 2.0, 3.0]});
    }
    #[test]
    fn test_length(){
        let v8 = Vec3New::from(3.0,4.0,5.0);
        let len = v8.length();
        assert_eq!(len, (50 as f64).sqrt());
    }
    #[test]
    fn test_fmt(){
        let v9 = Vec3New::from(1.0, 2.0, 3.0);
        assert_eq!(format!("{}", v9), "1 2 3")
    }
    #[test]
    fn test_add(){
        let v10 = Vec3New::from(1.0, 1.0, 1.0);
        let v11 = Vec3New::from(2.0, 2.0, 2.0);
        assert_eq!(&v10+&v11, Vec3New::from(3.0, 3.0, 3.0)); // 没有消耗掉原对象
        assert_eq!(v10+v11, Vec3New::from(3.0, 3.0, 3.0));
    }
    #[test]
    fn test_sub(){
        let v12 = Vec3New::from(1.0, 1.0, 1.0);
        let v13 = Vec3New::from(1.0, 1.0, 1.0);
        assert_eq!(&v12-&v13,Vec3New{e:[0.0, 0.0, 0.0]}); // 没有消耗掉原对象
        assert_eq!(v12-v13,Vec3New{e:[0.0, 0.0, 0.0]});
        assert_eq!(v12-&v13,Vec3New{e:[0.0, 0.0, 0.0]});
    }
    #[test]
    fn test_mul(){
        let v14 = Vec3New::from(1.0, 2.0, 3.0);
        let v15 = Vec3New::from(2.0, 4.0, 6.0);
        assert_eq!(&v14*&v15,Vec3New{e:[2.0, 8.0, 18.0]});
        assert_eq!(&v14*(3 as f64),Vec3New{e:[3.0, 6.0, 9.0]});
        assert_eq!((3 as f64)*&v14,Vec3New{e:[3.0, 6.0, 9.0]});
    }
    #[test]
    fn test_div(){
        let v16 = Vec3New::from(2.0, 4.0, 8.0);
        assert_eq!(&v16/2_f64,Vec3New{e:[1.0, 2.0, 4.0]});
    }
    #[test]
    fn test_dot(){
        let v17 = Vec3New::from(1_f64, 2_f64, 3_f64);
        let v18 = Vec3New::from(1_f64, 2_f64, 3_f64);
        let result = dot(&v17, &v18); 
        assert_eq!(result, 14_f64);
    }
    #[test]
    fn test_cross(){
        let v19 = Vec3New::from(1_f64, 2_f64, 3_f64);
        let v20 = Vec3New::from(1_f64, 2_f64, 3_f64);
        let result = cross(&v19,&v20); 
        assert_eq!(result, Vec3New{e:[0_f64, 0_f64, 0_f64]});
    }
    #[test]
    fn test_unitvector(){
        let v21 = Vec3New::from(3_f64, 4_f64, 12_f64);
        let result = unit_vector(&v21);
        assert_eq!(result, &v21/13_f64);
    }
    #[test]
    fn test_alias(){
        let v22 = Color3::from(3_f64, 4_f64, 12_f64);
        assert_eq!(v22, Color3::from(3_f64, 4_f64, 12_f64));
    }
}